package com.gamesite.gamesite.games.superTicTacToe.socket.endpoint;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gamesite.gamesite.core.pojo.GameRoom;
import com.gamesite.gamesite.core.pojo.User;
import com.gamesite.gamesite.core.service.UserService;
import com.gamesite.gamesite.core.vo.RoomPlayerVo;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/ws/UltimateTicTacToe")
@Component
public class WsUltimateTicTacToeEndpoint {
    private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<>());
    private static final Map<Session, User> userMap = new ConcurrentHashMap<>();
    private static final Map<Long, GameRoom> roomMap = new ConcurrentHashMap<>();
    private static UserService userService;

    // 存储每个房间的游戏状态
    private static final Map<Long, UltimateTicTacToeGameState> gameStates = new ConcurrentHashMap<>();

    @Autowired
    public void initWsUltimateTicTacToeEndpoint(UserService userService) {
        WsUltimateTicTacToeEndpoint.userService = userService;
    }

    @OnOpen
    public void onOpen(Session session) {
        sessions.add(session);
        System.out.println("Connected: " + session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("Message from " + session.getId() + ": " + message);
        try {
            JSONObject jsonMessage = JSONObject.parseObject(message);
            String type = jsonMessage.getString("type");

            switch (type) {
                case "setId":
                    bindSessionUserInfo(session, jsonMessage);
                    break;
                case "startGame":
                    handleStartGame(session, jsonMessage);
                    break;
                case "newStep":
                    handleNewStep(session, jsonMessage);
                    break;
                case "leaveGame":
                    handleLeaveGame(session, jsonMessage);
                    break;
                case "endGame":
                    handleEndGame(session, jsonMessage);
                    break;
                case "restoreSession":
                    handleRestoreSession(session, jsonMessage);
                default:
                    System.out.println("Unknown message type: " + type);
            }
        } catch (Exception e) {
            e.printStackTrace();
            sendError(session, "处理消息时出错: " + e.getMessage());
        }
    }

    @OnClose
    public void onClose(Session session) {
        User user = userMap.get(session);
        sessions.remove(session);
        userMap.remove(session);
        System.out.println("Disconnected: " + session.getId());

        // 通知其他玩家该用户已离开
//        if (user != null) {
//            notifyPlayerLeave(user.getId(), user.getUsername() + " 离开了游戏");
//        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
        sendError(session, "WebSocket错误: " + error.getMessage());
    }

    private void bindSessionUserInfo(Session session, JSONObject jsonMessage) {
        Long userId = jsonMessage.getLong("userId");
        User user = userService.getById(userId);
        if (user != null) {
            userMap.put(session, user);
            System.out.println("绑定用户: " + user.getUsername() + " 到会话: " + session.getId());
        } else {
            System.err.println("User not found with ID: " + userId);
            sendError(session, "用户ID不存在: " + userId);
        }
    }

    private void handleStartGame(Session session, JSONObject jsonMessage) throws IOException {
        Long roomId = jsonMessage.getLong("roomId");
        List<RoomPlayerVo> playerList = jsonMessage.getJSONArray("playerList").toJavaList(RoomPlayerVo.class);

        // 验证玩家数量
        if (playerList.size() != 2) {
            sendError(session, "超级井字棋需要2名玩家");
            return;
        }

        // 创建或获取房间
        GameRoom room = roomMap.computeIfAbsent(roomId, id -> {
            GameRoom newRoom = new GameRoom();
            newRoom.setRoomId(roomId);
            newRoom.setPlayerList(playerList);
            return newRoom;
        });

        // 初始化游戏状态
        UltimateTicTacToeGameState gameState = new UltimateTicTacToeGameState();
        gameState.setPlayerList(playerList);

        // 随机选择先手玩家
        int firstPlayerIndex = new Random().nextInt(2);
        Long firstPlayerId = playerList.get(firstPlayerIndex).getId();
        gameState.setCurrentPlayerId(firstPlayerId);
        gameState.setFirstPlayerId(firstPlayerId);
        gameStates.put(roomId, gameState);

        // 准备响应消息
        Map<String, Object> response = new HashMap<>();
        response.put("type", "startGame");
        response.put("roomId", roomId);
        response.put("playerList", playerList);
        response.put("firstPlayerId", firstPlayerId);

        // 如果该玩家为房主 将信息发送给所有玩家
        if (userMap.get(session) != null && userMap.get(session).getId().equals(room.getPlayerList().get(0).getId())) {
            broadcastToRoom(roomId, response);
        }



    }

    private void handleNewStep(Session session, JSONObject jsonMessage) throws IOException {
        Long roomId = jsonMessage.getLong("roomId");
        Long playerId = jsonMessage.getLong("playerId");
        String move = jsonMessage.getString("move"); // 格式: "boardRow,boardCol,cellRow,cellCol"
        Long nextBoard = jsonMessage.getLong("nextBoard"); // 下一个棋盘索引，-1表示任意

        UltimateTicTacToeGameState gameState = gameStates.get(roomId);
        if (gameState == null) {
            sendError(session, "房间 " + roomId + " 的游戏状态不存在");
            return;
        }

        // 验证当前玩家
        if (!playerId.equals(gameState.getCurrentPlayerId())) {
            sendError(session, "现在不是你的回合");
            return;
        }

        // 解析移动位置
        String[] parts = move.split(",");
        if (parts.length != 4) {
            sendError(session, "无效的移动格式");
            return;
        }

        int boardRow = Integer.parseInt(parts[0]);
        int boardCol = Integer.parseInt(parts[1]);
        int cellRow = Integer.parseInt(parts[2]);
        int cellCol = Integer.parseInt(parts[3]);
        int boardIndex = boardRow * 3 + boardCol;

        // 验证移动是否合法
        if (!gameState.isMoveValid(boardIndex, cellRow, cellCol)) {
            sendError(session, "非法的移动");
            return;
        }

        // 执行移动
        gameState.makeMove(boardIndex, cellRow, cellCol, playerId.equals(gameState.getPlayerList().get(0).getId()) ? 'X' : 'O');

        // 检查小棋盘是否有胜负
        gameState.checkSmallBoardWinner(boardIndex);

        // 检查大棋盘是否有胜负
        boolean gameEnded = gameState.checkUltimateWinner();

        // 检查下一个棋盘是否可用 如果不可用则下一个棋盘为任意
        Boolean isNextBoardNotActive;
        if(nextBoard != -1){
            isNextBoardNotActive = gameState.checkSmallBoardWinner(Math.toIntExact(nextBoard));
           if (isNextBoardNotActive) {
                nextBoard = -1L;
            }
        }
        // 准备响应消息
        Map<String, Object> response = new HashMap<>();
        response.put("type", "newStep");
        response.put("roomId", roomId);
        response.put("move", move);
        response.put("currentPlayerId", playerId);

        if (gameEnded) {
            response.put("gameOver", true);
            response.put("winnerId", gameState.getWinnerId());
        } else {
            // 设置下一个玩家和下一个棋盘
            Long nextPlayerId = gameState.getNextPlayerId();
            response.put("nextPlayerId", nextPlayerId);
            response.put("nextBoard", nextBoard);
            gameState.setCurrentPlayerId(nextPlayerId);
        }

        // 发送给所有玩家
        broadcastToRoom(roomId, response);
    }

    private void handleEndGame(Session session, JSONObject jsonMessage) throws IOException {
        Long roomId = jsonMessage.getLong("roomId");
        Long winnerId = jsonMessage.getLong("winnerId");

        Map<String, Object> response = new HashMap<>();
        response.put("type", "endGame");
        response.put("roomId", roomId);
        response.put("winnerId", winnerId);

//        broadcastToRoom(roomId, response);
        session.getBasicRemote().sendText(jsonMessage.toString());
        // 清理游戏状态
        gameStates.remove(roomId);
    }

    private void handleLeaveGame(Session session, JSONObject jsonMessage) throws IOException {
        Long roomId = jsonMessage.getLong("roomId");
        Long playerId = jsonMessage.getLong("playerId");
        User user = userMap.get(session);

        // 通知其他玩家
        Map<String, Object> response = new HashMap<>();
        response.put("type", "playerLeave");
        response.put("roomId", roomId);
        response.put("playerId", playerId);
        response.put("message", (user != null ? user.getUsername() : "玩家") + " 离开了游戏");

        broadcastToRoom(roomId, response);
        // 清理房间和游戏状态
        if (roomMap.get(roomId) != null && roomMap.get(roomId).getPlayerList().size() <= 1) {
            roomMap.remove(roomId);
            gameStates.remove(roomId);
        }
    }

    private void handleRestoreSession(Session session, JSONObject jsonMessage) throws IOException {
        Long userId = jsonMessage.getLong("userId");
        Long roomId = jsonMessage.getLong("roomId");
        System.out.println("恢复会话: " + userId + " 在房间 " + roomId);
        // 获取房间和游戏状态
        GameRoom room = roomMap.get(roomId);
        UltimateTicTacToeGameState gameState = gameStates.get(roomId);

        if (room == null || gameState == null) {
            sendError(session, "房间或游戏状态不存在");
            return;
        }

        // 准备恢复数据
        Map<String, Object> response = new HashMap<>();
        response.put("type", "sessionRestored");
        response.put("roomId", roomId);
        response.put("playerList", room.getPlayerList());
        response.put("currentPlayerId", gameState.getCurrentPlayerId());
        response.put("activeBoard", gameState.getActiveBoard());
        response.put("isFirstMove", false);
        response.put("firstPlayerId", gameState.getFirstPlayerId());

        // 添加棋盘状态
        List<List<List<Character>>> boardState = new ArrayList<>();
        for (int i = 0; i < 9; i++) {
            List<List<Character>> smallBoard = new ArrayList<>();
            for (int row = 0; row < 3; row++) {
                List<Character> cellRow = new ArrayList<>();
                for (int col = 0; col < 3; col++) {
                    cellRow.add(gameState.getBoards()[i].cells[row][col]);
                }
                smallBoard.add(cellRow);
            }
            boardState.add(smallBoard);
        }
        response.put("boardState", boardState);

        // 添加小棋盘胜负状态
        List<Character> smallBoardWinners = new ArrayList<>();
        for (int i = 0; i < 9; i++) {
            smallBoardWinners.add(gameState.getBoards()[i].winner);
        }
        response.put("smallBoardWinners", smallBoardWinners);

        // 发送恢复数据
        session.getBasicRemote().sendText(new ObjectMapper().writeValueAsString(response));
    }

    private void broadcastToRoom(Long roomId, Object message) throws IOException {
        GameRoom room = roomMap.get(roomId);
        if (room == null) return;

        ObjectMapper mapper = new ObjectMapper();
        String messageStr = mapper.writeValueAsString(message);

        for (RoomPlayerVo player : room.getPlayerList()) {
            for (Map.Entry<Session, User> entry : userMap.entrySet()) {
                if (entry.getValue().getId().equals(player.getId())) {
                    entry.getKey().getBasicRemote().sendText(messageStr);
                    break;
                }
            }
        }
    }

    private void broadcastToRoomExcept(Long roomId, Session exceptSession, Object message) throws IOException {
        GameRoom room = roomMap.get(roomId);
        if (room == null) return;

        ObjectMapper mapper = new ObjectMapper();
        String messageStr = mapper.writeValueAsString(message);

        for (RoomPlayerVo player : room.getPlayerList()) {
            for (Map.Entry<Session, User> entry : userMap.entrySet()) {
                if (entry.getValue().getId().equals(player.getId()) && !entry.getKey().equals(exceptSession)) {
                    entry.getKey().getBasicRemote().sendText(messageStr);
                    break;
                }
            }
        }
    }

    private void notifyPlayerLeave(Long playerId, String message) {
        try {
            Map<String, Object> msg = new HashMap<>();
            msg.put("type", "playerLeave");
            msg.put("playerId", playerId);
            msg.put("message", message);

            ObjectMapper mapper = new ObjectMapper();
            String messageStr = mapper.writeValueAsString(msg);

            for (Map.Entry<Session, User> entry : userMap.entrySet()) {
                if (!entry.getValue().getId().equals(playerId)) {
                    entry.getKey().getBasicRemote().sendText(messageStr);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sendError(Session session, String errorMessage) {
        try {
            Map<String, Object> error = new HashMap<>();
            error.put("type", "error");
            error.put("message", errorMessage);

            ObjectMapper mapper = new ObjectMapper();
            session.getBasicRemote().sendText(mapper.writeValueAsString(error));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 超级井字棋游戏状态类
     */
    @Data
    private static class UltimateTicTacToeGameState {
        // Getters and Setters
        private List<RoomPlayerVo> playerList;
        private Long currentPlayerId;
        private Long winnerId;
        private Integer activeBoard; // 当前需要下棋的棋盘索引，null表示任意
        private Long firstPlayerId; // 先手玩家ID

        // 9个小棋盘，每个小棋盘有3x3格子
        private final SmallBoard[] boards = new SmallBoard[9];

        public UltimateTicTacToeGameState() {
            for (int i = 0; i < 9; i++) {
                boards[i] = new SmallBoard();
            }
        }

        // 检查移动是否合法
        public boolean isMoveValid(int boardIndex, int row, int col) {
            // 检查棋盘索引是否有效
            if (boardIndex < 0 || boardIndex >= 9) {
                System.out.println("Invalid board index: " + boardIndex);
                return false;
            }

            // 如果指定了活跃棋盘，必须匹配
//            if (activeBoard != null && activeBoard != boardIndex) {
//                System.out.println("Invalid move: active board is " + activeBoard + ", but tried to move on board " + boardIndex);
//                return false;
//            }

            // 检查小棋盘是否已有胜负
            if (boards[boardIndex].winner != null) {
                System.out.println("Invalid move: board " + boardIndex + " already has a winner: " + boards[boardIndex].winner);
                return false;
            }

            // 检查格子是否为空
            return boards[boardIndex].cells[row][col] == null;
        }

        // 执行移动
        public void makeMove(int boardIndex, int row, int col, char symbol) {
            boards[boardIndex].cells[row][col] = symbol;

            // 下一个活跃棋盘由当前移动的格子位置决定
            activeBoard = row * 3 + col;

            // 如果下一个棋盘已有胜负，则活跃棋盘为null
            if (boards[activeBoard].winner != null) {
                activeBoard = null;
            }
        }

        // 检查小棋盘是否有胜负
        public boolean checkSmallBoardWinner(int boardIndex) {
            SmallBoard board = boards[boardIndex];

            // 检查行
            for (int i = 0; i < 3; i++) {
                if (board.cells[i][0] != null &&
                        board.cells[i][0] == board.cells[i][1] &&
                        board.cells[i][0] == board.cells[i][2]) {
                    board.winner = board.cells[i][0];
                    return true;
                }
            }

            // 检查列
            for (int j = 0; j < 3; j++) {
                if (board.cells[0][j] != null &&
                        board.cells[0][j] == board.cells[1][j] &&
                        board.cells[0][j] == board.cells[2][j]) {
                    board.winner = board.cells[0][j];
                    return true;
                }
            }

            // 检查对角线
            if (board.cells[0][0] != null &&
                    board.cells[0][0] == board.cells[1][1] &&
                    board.cells[0][0] == board.cells[2][2]) {
                board.winner = board.cells[0][0];
                return true;
            }

            if (board.cells[0][2] != null &&
                    board.cells[0][2] == board.cells[1][1] &&
                    board.cells[0][2] == board.cells[2][0]) {
                board.winner = board.cells[0][2];
                return true;
            }

            // 检查是否平局
            boolean isFull = true;
            outer: for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (board.cells[i][j] == null) {
                        isFull = false;
                        break outer;
                    }
                }
            }
            if (isFull) {
                board.winner = 'D'; // D表示平局
                return true;
            }
            return false;
        }

        // 检查大棋盘是否有胜负
        public boolean checkUltimateWinner() {
            // 收集每个小棋盘的胜利者
            Character[] winners = new Character[9];
            for (int i = 0; i < 9; i++) {
                winners[i] = boards[i].winner;
            }

            // 检查行
            for (int i = 0; i < 3; i++) {
                int idx = i * 3;
                if (winners[idx] != null && winners[idx] != 'D' &&
                        winners[idx] == winners[idx + 1] &&
                        winners[idx] == winners[idx + 2]) {
                    winnerId = winners[idx] == 'X' ? playerList.get(0).getId() : playerList.get(1).getId();
                    return true;
                }
            }

            // 检查列
            for (int j = 0; j < 3; j++) {
                if (winners[j] != null && winners[j] != 'D' &&
                        winners[j] == winners[j + 3] &&
                        winners[j] == winners[j + 6]) {
                    winnerId = winners[j] == 'X' ? playerList.get(0).getId() : playerList.get(1).getId();
                    return true;
                }
            }

            // 检查对角线
            if (winners[0] != null && winners[0] != 'D' &&
                    winners[0] == winners[4] &&
                    winners[0] == winners[8]) {
                winnerId = winners[0] == 'X' ? playerList.get(0).getId() : playerList.get(1).getId();
                return true;
            }

            if (winners[2] != null && winners[2] != 'D' &&
                    winners[2] == winners[4] &&
                    winners[2] == winners[6]) {
                winnerId = winners[2] == 'X' ? playerList.get(0).getId() : playerList.get(1).getId();
                return true;
            }

            // 检查是否平局
            boolean allDecided = true;
            for (Character winner : winners) {
                if (winner == null) {
                    allDecided = false;
                    break;
                }
            }

            if (allDecided) {
                winnerId = null; // 平局
                return true;
            }

            return false;
        }

        // 获取下一个玩家ID
        public Long getNextPlayerId() {
            int currentIndex = -1;
            for (int i = 0; i < playerList.size(); i++) {
                if (playerList.get(i).getId().equals(currentPlayerId)) {
                    currentIndex = i;
                    break;
                }
            }

            if (currentIndex == -1) {
                return playerList.get(0).getId(); // 默认返回第一个玩家
            }

            return playerList.get((currentIndex + 1) % playerList.size()).getId();
        }

    }

    /**
     * 小棋盘类
     */
    private static class SmallBoard {
        private Character[][] cells = new Character[3][3];
        private Character winner;

        public SmallBoard() {
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    cells[i][j] = null;
                }
            }
        }


    }
}
